#!/usr/bin/python3
# coding=utf-8
"""
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# Author: YangYunYi
# Create: 2020-11-04
"""

import os
import subprocess
import yaml
from autoimport.logmodule import logger
import autoimport.getdependinfo.utils as utils

CURRENT_PATH = os.path.dirname(os.path.abspath(__file__))
OUTPUT_PATH = "output"
CONFIG_PATH = "config"
TEMP_PATH = "temp"
LOG_PATH = "log"
# 日志
this_logger = logger.init('getrequires')
logger.add_file_handler(this_logger, "getrequires", "getrequires")


def init_branches():
    """
    初始化搜索分支
    :return: 无
    """
    conf_path = utils.get_file_path(CONFIG_PATH, 'branchesDefine.yaml')
    if not os.path.isfile(conf_path):
        this_logger.error(
            "Not found the branchesDefine.yaml file:%s.", conf_path)
        return None, None

    with open(conf_path, "r") as conf_file:
        file_data = yaml.safe_load(conf_file)
        if 'credible_branches' in file_data:
            credible_branches = utils.list_unique(
                file_data["credible_branches"])
            this_logger.info("Init credible branches num:%d.",
                             len(credible_branches))
        else:
            this_logger.warning("Not found the credible_branches.")

        if 'working_branches' in file_data:
            working_branches = utils.list_unique(
                file_data["working_branches"])
            this_logger.info("Init working branches num:%d.",
                             len(working_branches))
        else:
            this_logger.warning("Not found the working_branches.")
    return credible_branches, working_branches


def get_package_exist_branches(pkg_name):
    """
       osc search获取一个软件包已经存在的分支
       :param pkg_name: 待查询包名
       :return: 搜索到的分支列表集合
       """
    if pkg_name == "":
        this_logger.error("pkg_name is null.")
        return False
    search_cmd = "osc search " + pkg_name
    branches = set()
    try:
        output = subprocess.check_output(
            search_cmd, stderr=subprocess.STDOUT, shell=True)
        for line in output.splitlines():
            line_str = line.decode('utf-8')
            if (line_str.find(pkg_name) != -1) and \
                    (line_str.find('No matches found') == -1) and \
                    (line_str.find('matches for') == -1):
                branch_name = str(line_str.strip().split()[0])
                branches.add(branch_name)
                # this_logger.debug("Add branches:%s.", branch_name)

        message = "    ".join(branches)
        this_logger.debug(
            "Get package:%s branches by osc search end. Results:%s.", pkg_name, message)
        this_logger.info(
            "Get package:%s branches by osc search end. Total num:%d.", pkg_name, len(branches))
    except subprocess.TimeoutExpired as time_err:
        this_logger.error(
            "Command:%s is time out.error: %s", search_cmd, time_err)
    except subprocess.CalledProcessError as call_err:
        this_logger.error(
            "Command:%s is failed.error: %s", search_cmd, call_err.output.decode(encoding="utf-8"))

    return branches


def get_pkg_build_status(pkg_name, branch_name):
    """
    判断软件包在指定分支的工程下是否编译成功
    :param pkg_name: 软件包名称
    :param branch_name: 分支名称
    :return: True-构建成功, False-构建失败
    """
    if pkg_name == "" or branch_name == "":
        this_logger.error("pkg_name or branch_name is null.")
        return False
    query_cmd = "osc results " + branch_name + " " + pkg_name
    try:
        output = subprocess.check_output(
            query_cmd, stderr=subprocess.STDOUT, shell=True)
        for line in output.splitlines():
            line_str = line.decode('utf-8')
            if (line_str.find('standard_aarch64') != -1) or \
                    (line_str.find('standard_x86_64') != -1):
                if line_str.find('succeed') != -1:
                    this_logger.debug(
                        "Found exist package: %s in branch:%s.", pkg_name, branch_name)
                    return True
        this_logger.debug(
            "Branch %s build package %s result is not succeed.", branch_name, pkg_name)
        return False
    except subprocess.TimeoutExpired as time_err:
        this_logger.error(
            "Command:%s is time out.error: %s", query_cmd, time_err)
    except subprocess.CalledProcessError as call_err:
        this_logger.error(
            "Command:%s is failed.error: %s", query_cmd, call_err.output.decode(encoding="utf-8"))
    return False


def dump_new_pkg(new_pkg):
    """
    将需要新引入的包输出到结果中
    :return: 无
    """
    file_path = utils.get_file_path(OUTPUT_PATH, "new_rpm_list.txt")
    with open(file_path, "w") as new_pkg_file:
        for package in new_pkg:
            data = package + "\n"
            new_pkg_file.write(data)
    this_logger.info(
        "Dump new package succeed.sum: %d", len(new_pkg))


def dump_found_pkg(found_credible_pkg, found_working_pkg):
    """
    将已经存在的包输出到结果中
    :return: 无
    """
    file_path = utils.get_file_path(OUTPUT_PATH, "foundPackage.txt")
    with open(file_path, "w") as found_pkg_file:
        found_pkg_file.write(
            "[RESULT] Here are the existing packages in credible branches found:\n")
        for i in found_credible_pkg:
            data = i[0] + " " + i[1] + "\n"
            found_pkg_file.write(data)
        found_pkg_file.write(
            "=======================================================\n")
        this_logger.info("Dump existing packages in credible branches succeed. sum:%d.", len(
            found_credible_pkg))

        found_pkg_file.write(
            "[RESULT] Here are the existing packages in working branches found:\n")
        for i in found_working_pkg:
            data = i[0] + " " + i[1] + "\n"
            found_pkg_file.write(data)
        found_pkg_file.write(
            "=======================================================\n")
        this_logger.info("Dump existing packages in working branches succeed. sum:%d.", len(
            found_working_pkg))


def is_factory_to_mainline(package, found_credible_pkg):
    """
    是否需要从Factory分支合入Mainline, 即在Factory中有，在Mainline中没有
    :param found_credible_pkg: 可信分支包的列表
    :param package:软件包
    :return: True-需要，False-不需要
    """
    if ([package, 'openEuler:Factory'] in found_credible_pkg) and \
            ([package, 'openEuler:Mainline'] not in found_credible_pkg):
        return True
    return False


def dump_factory_to_mainline(factory_to_mainline_pkg):
    """
    需要从Factory合入Mainline的包输出到文件
    :return: 无
    """
    file_path = utils.get_file_path(OUTPUT_PATH, "foundPackage.txt")
    with open(file_path, "a") as found_pkg_file:
        found_pkg_file.write(
            "[Attention] The following packages may need to be merged "
            "into Mainline from Factory :\n")
        for package in factory_to_mainline_pkg:
            data = package + "\n"
            found_pkg_file.write(data)
        found_pkg_file.write(
            "=======================================================\n")
        this_logger.info("Dump Factory branch to Mainline branch succeed. sum:%d.", len(
            factory_to_mainline_pkg))


def dump_credible_to_working(credible_to_working_pkg):
    """
    可以从可信分支拉取到工作分支的包，输出到文件
    :return: 无
    """
    file_path = utils.get_file_path(OUTPUT_PATH, "foundPackage.txt")
    with open(file_path, "a") as found_pkg_file:
        found_pkg_file.write(
            "[Attention] The following packages have been pulled from "
            "credible branch to working branch:\n")
        for i in credible_to_working_pkg:
            data = i[0] + " " + i[1] + "\n"
            found_pkg_file.write(data)
        found_pkg_file.write(
            "=======================================================\n")
        this_logger.info("Dump credible branch to working branch succeed. sum:%d.", len(
            credible_to_working_pkg))


def identify_valid_branch(input_pkg_list):
    """
    识别出存在已经引入的包的分支
    :return: 无
    """
    # 需要新引入的包
    new_pkg = []
    # 需要从Factory分支合入Mainline的包
    factory_to_mainline_pkg = set()
    # 可以从可信分支拉到工作分支的包
    credible_to_working_pkg = []
    # 可信分支中已编译成功的包
    found_credible_pkg = []
    # 工作分支中已编译成功的包
    found_working_pkg = []

    # 初始化搜索分支
    credible_branches, working_branches = init_branches()
    if not credible_branches or not working_branches:
        this_logger.error(
            "Init branches failed.")
        return False

    for package in input_pkg_list:
        credible_branch_found_name = ""
        credible_branch_found = False
        working_branch_found = False
        working_branch_existed = False
        # 1.获取这个包存在的分支
        branches = get_package_exist_branches(package)

        for branch in branches:
            if branch in credible_branches and get_pkg_build_status(package, branch):
                found_credible_pkg.append([package, branch])
                credible_branch_found_name = branch \
                    if credible_branch_found_name == "" \
                    else credible_branch_found_name
                credible_branch_found = True

            if branch in working_branches:
                working_branch_existed = True
                if get_pkg_build_status(package, branch):
                    found_working_pkg.append([package, branch])
                    working_branch_found = True

        if not (credible_branch_found or working_branch_found):
            new_pkg.append(package)
            this_logger.info(
                "Package %s needs to be introduced.", package)
        elif credible_branch_found:
            if is_factory_to_mainline(package, found_credible_pkg):
                factory_to_mainline_pkg.add(package)
                this_logger.info(
                    "Package %s needs to be merge from Factory to Mainline.", package)

            if not working_branch_found:
                branch_exist_package(credible_branch_found_name, package, working_branch_existed, 
                                     credible_to_working_pkg, working_branches)
                this_logger.info(
                    "Package %s can be pull from branch:%s.", package, credible_branch_found_name)

    dump_new_pkg(new_pkg)
    dump_found_pkg(found_credible_pkg, found_working_pkg)
    dump_factory_to_mainline(factory_to_mainline_pkg)
    dump_credible_to_working(credible_to_working_pkg)
    return True


def osc_branch(package, credible_branch, working_branch):
    """
    调用osc branch命令将credible_branch工程下的package包拉到working_branch
    :param package: 软件包名
    :param credible_branch: 已经编好包的分支
    :param working_branch: 想要这个包的分支
    :return: True-成功，False-失败
    """
    if package == "" or credible_branch == "" or working_branch == "":
        this_logger.error("package or credible_branch or working_branch is null.")
        return False
    branch_cmd = "osc branch " + credible_branch + \
                 " " + package + " " + working_branch
    try:
        output = subprocess.check_output(
            branch_cmd, stderr=subprocess.STDOUT, shell=True)
        for line in output.splitlines():
            line_str = line.decode('utf-8')
            if (line_str.find("failed") != -1) or \
                    (line_str.find('error') != -1):
                this_logger.error("osc branch failed. %s", line_str)
                return False
        this_logger.info(
            "Process osc branch :%s succeed.", branch_cmd)
        return True
    except subprocess.TimeoutExpired as time_err:
        this_logger.error(
            "Command:%s is time out.error: %s", branch_cmd, time_err)
    except subprocess.CalledProcessError as call_err:
        this_logger.error(
            "Command:%s is failed.error: %s", branch_cmd, call_err.output.decode(encoding="utf-8"))
    return False


def branch_exist_package(credible_branch, package, working_branch_existed, 
                         credible_to_working_pkg, working_branches):
    """
    从可信分支拉已经编好的包
    :param working_branches: 工作分支
    :param credible_to_working_pkg: 可以从可信分支拉到工作分支的包列表
    :param credible_branch:
    :param package: 待拉取包名
    :param working_branch_existed: 在分支上已经建仓
    :return: 无
    """
    # 可信分支选择
    for branch in working_branches:
        print('****This package: {0} can pull form crediable branch: '
              '{1} to working branch:{2}'.format(package, credible_branch, branch))

        if working_branch_existed:
            need_branch = 'no'
            print('****This package: {0} existed in working branch:{1}, '
                  'you should delete it first.'.format(package, branch))
        else:
            need_branch = input("****Do you want me to pull it now? yes/no ")

        if need_branch in ['y', 'Y', 'yes', 'Yes', 'YES']:
            if osc_branch(package, credible_branch, branch):
                status = credible_branch + " -> " + branch + " (done)"
            else:
                status = credible_branch + " -> " + branch + " (todo)"
        else:
            print("****You can pull it later, and find more info in foundPackage.txt")
            status = credible_branch + " -> " + branch + " (todo)"
        # 将这个已存在的包记录下来
        credible_to_working_pkg.append([package, status])
        this_logger.debug(
            "Need branch package:%s status:%s.", package, status)


def main():
    """
    main函数
    :return: 无
    """
    # 获取输入
    input_pkg_list_file = utils.get_file_path(TEMP_PATH, 'sort_list.txt')
    input_pkg_list = utils.get_input_pkg_list(input_pkg_list_file)
    if input_pkg_list is None:
        this_logger.error("Get input pkg list failed.")
        return False
    # 识别依赖包是否已经存在，以及所在分支
    if not identify_valid_branch(input_pkg_list):
        return False
    return True

# if __name__ == '__main__':
#    main()
